//结构体
//结构体 (struct) 是一种记录类型，所包含的每个域 (field) 都有一个名称。 每个结构体也都有一个名称，
// 通常以大写字母开头，使用驼峰命名法。

use std::cell::Cell;

// 元组结构体 (tuple struct) 是由元组和结构体混合构成，元组结构体有名称， 但是它的域没有。
// 当元组结构体只有一个域时，称为新类型 (newtype)。 没有任何域的结构体，称为类单元结构体 (unit-like struct)。
// 结构体中的值默认是不可变的，需要给结构体加上mut使其可变。
pub fn struct_test() {
    test();
    test1();
    test2();
    test3();
    test4();
    test5();
    outside_fn();
    enum_fn();
}

fn test3(){
    //元组结构体
    struct Color(u8, u8, u8);
    let android_green = Color(0xa4, 0xc6, 0x39);
    //解构元组结构体
    let Color(red, green, blue) = android_green;
    assert_eq!(red, 0xa4);

    //A tuple struct’s constructors can be used as functions.
    struct Digit(i32);
    let v = vec![0, 1, 2];
    let d: Vec<Digit> = v.into_iter().map(Digit).collect();

    // newtype: a tuple struct with only one element
    struct Inches(i32);
    let length = Inches(10);
    //解构
    let Inches(integer_length) = length;
    assert_eq!(integer_length, 10);
}

fn test2(){
    //User 结构体的定义中，我们使用了自身拥有所有权的 String 类型而不是 &str 字符串 slice 类型。
    // 这是一个有意而为之的选择，因为我们想要这个结构体拥有它所有的数据，为此只要整个结构体是有效的话其数据也是有效的。
    struct User{
        name: String,
        age: i32,
    }
    let caedmon = User{name: String::from("caedmon"), age: 30};
}

fn test1(){
    //结构体
    struct Point{
        x: i32,
        y: i32,
    }

    let point = Point{x:0, y:0};
    let mut point = Point{x:1, y:2};
    point.x = 10;
    //此处没有解构，所以用point.x的方式来获取值
    assert_eq!(point.x, 10);
}

//一个包含..的struct可以用来从其它结构体拷贝一些值或者在解构时忽略一些域：
fn test() {
    #[derive(Default)]
    struct Point3d {
        x: i32,
        y: i32,
        z: i32,
    }

    let origin = Point3d::default();
    let point = Point3d { y: 1, ..origin };
    //解构结构体
    let Point3d { x: x0, y: y0, .. } = point;
    assert_eq!(x0, 0);
    assert_eq!(y0, 1);
    //解构结构体（简写方式）
    let Point3d{x, y, z} = point;
    assert_eq!(x, 0);
    assert_eq!(y, 1);
    assert_eq!(z, 0);
}

fn test4(){
    // unit-like structs
    struct EmptyStruct;
    let empty = EmptyStruct;
}
//需要注意，Rust在语言级别不支持域可变性 (field mutability)，所以不能这么写：
//struct Point {
//  mut x: i32,
//  y: i32,
//}
//这是因为可变性是绑定的一个属性，而不是结构体自身的。可以使用Cell<T>来模拟：
fn test5(){
    struct Point{
        x: i32,
        y: Cell<i32>,
    }
    let point = Point{x: 5, y: Cell::new(6)};
    point.y.set(7);
    assert_eq!(point.y, Cell::new(7));
}

//结构体的域对其所在模块 (mod) 之外默认是私有的，可以使用pub关键字将其设置成公开。
mod graph{
    #[derive(Default)]
    pub struct Point {
        pub x: i32,
        y: i32,
    }

    pub fn inside_fn() {
        let p = Point{x: 10, y: 20};
        println!("{}, {}", p.x, p.y);
    }
}

fn outside_fn() {
    graph::inside_fn();
    let p = graph::Point::default();
    println!("{}", p.x);
    //因y是私有的，所以该句会报错
//    println!("{}", p.y);
}

//枚举
//Rust有一个集合类型，称为枚举 (enum)，代表一系列子数据类型的集合。
// 其中子数据结构可以为空-如果全部子数据结构都是空的，就等价于C语言中的enum。 我们需要使用::来获得每个元素的名称。
//与结构体一样，枚举中的元素默认不能使用关系运算符进行比较 (如==, !=, >=)，
// 也不支持像+和*这样的双目运算符，需要自己实现，或者使用match进行匹配。
//枚举默认也是私有的，如果使用pub使其变为公有，则它的元素也都是默认公有的。 这一点是与结构体不同的：即使结构体是公有的，
// 它的域仍然是默认私有的。这里的共有/私有仍然 是针对其定义所在的模块之外。此外，枚举和结构体也可以是递归的 (recursive)。
fn enum_fn() {
    enum Message {
        Quit,
        ChangeColor(i32, i32, i32),
        Move{x: i32, y: i32},
        Write(String),
    }

    let msg = Message::Move{x: 3, y: 4};
    //解构枚举
    match msg {
        Message::Move {x, y} => {
            assert_eq!(x, 3);
            assert_eq!(y, 4);
        }
        _ => {
            println!("忽略对其它的枚举类型的解构")
        }
    }

}